<!--
 * @Author: lp
 * @Date: 2022-02-08 11:24:02
 * @LastEditTime: 2022-06-20 16:38:28
 * @LastEditors: 瞿旺兴 18774621495@163.com
 * @Description: 全局通用模板
 * @FilePath: //nien-vue3-admin//src//components//customContainer//index.vue
-->
<template>
  <div class="app-container">
    <!-- 搜索 -->
    <customForm
      v-if="seleProps.config.query.fields.length > 0"
      v-show="showSearch"
      ref="customFormRef"
      class="container-card"
      :options="seleProps.config.query"
      :custom-key="seleProps.config.customKey"
      :popover-ref="popoverRef"
      :form-db-is-ready="formDbIsReady"
      :extend="extend"
      :column-count="columnCount"
      @get-list="getList"
      @reset-query="resetQuery"
      @put-away="putAway"
      @handle-custorm="handleCustorm"
    />
    <el-popover
      ref="popoverRef"
      v-model:visible="visible"
      width="500px"
      :virtual-ref="customFormRef?.$refs.custormBtnRef"
      virtual-triggering
    >
      <div class="custom-head">
        <span>{{ $t('layout.customSettings') }}</span>
        <el-icon @click="visible = false"><close /></el-icon>
      </div>
      <hr />
      <div class="custom-container">
        <h6>{{ $t('layout.formCustomization') }}</h6>
        <draggable
          v-model="customFormList"
          filter=".no_checked"
          group="prop"
          :move="checkMove"
          item-key="prop"
          @start="drag = true"
          @end="drag = false"
        >
          <template #item="{ element }">
            <el-check-tag
              v-if="element.draggable"
              class="m5"
              :class="[!element.show && 'no_checked']"
              :checked="element.show"
              @change="handleCustomFormChange(element)"
            >
              {{ seleProps.config.query.fields[element.id].label }}
            </el-check-tag>
          </template>
        </draggable>
      </div>
      <div>
        <h6>{{ $t('layout.tableCustomization') }}</h6>
        <draggable
          v-model="customTableList"
          filter=".no_checked"
          group="prop"
          :move="checkMove"
          item-key="prop"
          @start="drag = true"
          @end="drag = false"
        >
          <template #item="{ element }">
            <el-check-tag
              v-if="element.draggable"
              class="m5"
              :class="[!element.show && 'no_checked']"
              :checked="element.show"
              @change="handleCustomTableChange(element)"
            >
              {{ seleProps.config.table.columns[element.id].label }}
            </el-check-tag>
          </template>
        </draggable>
      </div>
      <div style="text-align: right; margin: 0;">
        <el-button text @click="customReset">{{ $t('public.reset') }}</el-button>
        <el-button size="small" @click="visible = false">{{ $t('public.cancel') }}</el-button>
        <el-button size="small" type="primary" @click="onCustomSave">{{ $t('public.save') }}</el-button>
      </div>
    </el-popover>
    <!-- 主体 -->
    <div class="container-card mt16">
      <!-- 操作按钮 -->
      <el-row v-if="Array.isArray(seleProps.config.btns) && seleProps.config.btns.length " :gutter="10" class="container-operation">
        <customOperation :options="seleProps.config.btns" />
      </el-row>
      <!-- 表格 -->
      <customTable
        v-if="seleProps.config.table && seleProps.config.table.columns.length"
        ref="customTableRef"
        :custom-key="seleProps.config.customKey"
        :options="seleProps.config.table"
        :loading="loading"
        :data="tableData"
        :table-db-is-ready="tableDbIsReady"
      />
      <!-- 分页 -->
      <pagination
        v-if="seleProps.config.query && seleProps.config.query.queryParams"
        v-show="total > 0"
        :page="seleProps.config.query.queryParams.pageNum"
        :limit="seleProps.config.query.queryParams.pageSize"
        :total="total"
        @update:page="updatePage"
        @update:limit="updateLimit"
        @pagination="getList"
      />
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, computed, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import { Close } from '@element-plus/icons-vue'
import draggable from 'vuedraggable'
import localforage from 'localforage'
import { depClone, validateForm } from '@/utils'

export default defineComponent({
  components: {
    draggable,
    Close,
  },
  props: {
    config: {
      type: Object,
      default: () => ({}),
    },
  },
  setup(props) {
    const seleProps = props
    const total = ref(0)
    const customFormRef = ref()
    const customTableRef = ref()
    const popoverRef = ref()
    const showSearch = ref(true)
    const tableData = ref([])
    const loading = ref(false)
    const visible = ref(false)

    const queryFn = async () => {
      const {
        api,
        query: { queryParams },
        requestTrans,
      } = seleProps.config
      try {
        loading.value = true
        let params = {}
        if (typeof requestTrans === 'function') {
          params = await requestTrans(depClone(queryParams))
        } else {
          params = queryParams
        }
        const res = await api(params)
        if (res.code === 200) {
          tableData.value = res.data
          total.value = res.total || 0
          // ElMessage.success(res.msg)
        } else {
          tableData.value = []
          total.value = 0
          ElMessage.error(res.msg)
        }
      } catch (error) {
        console.log(error)
        ElMessage.error(error)
      } finally {
        loading.value = false
      }
    }

    const getList = () => {
      if (seleProps.config.query?.formOption?.rules?.length) {
        nextTick(() => {
          validateForm(customFormRef.value.$refs.queryForm, () => {
            queryFn()
          })
        })
      } else {
        queryFn()
      }
    }
    const resetQuery = () => {
      customFormRef.value.$refs.queryForm.resetFields()
      seleProps.config.query.queryParams.pageNum = 1
      seleProps.config.query.queryParams.pageSize = 10
      getList()
    }
    /* 收起展开相关 */
    const columnCount = ref<number>(3)
    const extend = ref(true)
    const putAway = () => {
      extend.value = !extend.value
    }
    // 监控媒体查询动态判断每一列的输入框的个数
    window.addEventListener('resize', WidthChange())
    function WidthChange() {
      const innerWidth = window.innerWidth

      if (innerWidth < 768) {
        columnCount.value = 2
      } else if (innerWidth >= 768 && innerWidth < 1920) {
        columnCount.value = 3
      } else {
        columnCount.value = 4
      }
      return WidthChange
    }
    /* 获取列表 默认初始化调用 */
    if (seleProps.config.initList !== false) {
      getList()
    }
    /* 分页相关 */
    const updatePage = (v) => {
      seleProps.config.query.queryParams.pageNum = v
    }
    const updateLimit = (v) => {
      seleProps.config.query.queryParams.pageSize = v
    }
    // #region 自定义表格，表单排序相关 开始
    /**
     * @description: 筛选存入数据库的表单字段
     * @return {*} id: 源代码配置的位置，利用该项可以准确的找到源数据 prop： 标识 draggable:是否可拖拽 show:是否展示
     */

    const pageFormFields = computed(() => seleProps.config.query.fields.map((el, index) => ({
      id: index,
      prop: el.prop,
      draggable: true,
      show: true,
    })))
    /**
     * @description: 筛选存入数据库的表格字段
     * @return {*}
     */
    const pageTableFields = computed(() => seleProps.config.table.columns.map((el, index) => {
      const data:{id:number, prop:string, draggable:boolean, show:boolean, type?:string} = {
        id: index,
        prop: el.prop,
        draggable: true,
        show: true,
      }
      if (el.type) {
        data.type = el.type
      } 
      // 固定列，复选列，展开列禁止拖动并且不展示在拖动列表
      if (el.fixed || el.type === 'selection' || el.type === 'expand') {
        data.draggable = false
        data.show = true
      }
      return data
    }))
    const drag = ref(false)
    const formDbIsReady = ref(false)
    const tableDbIsReady = ref(false)
    const customFormList = ref([])
    const customTableList = ref([])
    const dbFormName = `${ seleProps.config.customKey }-form`
    const dbTableName = `${ seleProps.config.customKey }-table`
    // 这两张表只作为对比源代码和数据库代码的依据
    const dbTableOrigion = `${ dbTableName }origion`
    const dbFormOrigion = `${ dbFormName }origion`
    if (seleProps.config.customKey) {
      // 读取存储的源代码配置进行差异化更新
      localforage.getItem(dbFormOrigion).then(formOrigion => {
        if (formOrigion === null) {
        // 初始化
          localforage.setItem(dbFormOrigion, pageFormFields.value)
          localforage.setItem(dbFormName, pageFormFields.value).then(() => {
            formDbIsReady.value = true
          })
        } else if (JSON.stringify(formOrigion) !== JSON.stringify(pageFormFields.value)) {
        // 当发生差异的时候，首先更新源代码（方便下一次对比），然后更新用户配置
          localforage.setItem(dbFormOrigion, pageFormFields.value)
          localforage.getItem(dbFormName).then((dbForm:[any]|null) => {
          // 如果配置未建立，则以当前的数据为准
            if (dbForm === null) {
              localforage.setItem(dbFormName, pageFormFields.value).then(() => {
                formDbIsReady.value = true
              })
            } else {
            // 配置数据存在，则对比差异
            // 新已存在的字段
              const newFieldList = []
              // 不存在的字段
              const nonExistent = []
              pageFormFields.value.forEach((field) => {
                const propKey = dbForm.findIndex(el => el.prop === field.prop)
                if (propKey === -1) {
                  nonExistent.push(field)
                } else {
                  newFieldList.splice(propKey, 0, field)
                }
              })
              // type 字段放头部
              newFieldList.push(...nonExistent)
              localforage.setItem(dbFormName, newFieldList).then(() => {
                formDbIsReady.value = true
              })
            }
          })
        } else {
          formDbIsReady.value = true
        }
      })
      localforage.getItem(dbTableOrigion).then(res => {
        if (res === null) {
          localforage.setItem(dbTableOrigion, pageTableFields.value)
          localforage.setItem(dbTableName, pageTableFields.value).then(() => {
            tableDbIsReady.value = true
          })
        } else if (JSON.stringify(res) !== JSON.stringify(pageTableFields.value)) {
        // 当发生差异的时候，首先更新源代码（方便下一次对比），然后更新用户配置
          localforage.setItem(dbTableOrigion, pageTableFields.value)
          localforage.getItem(dbTableName).then((tableDb:[any]|null) => {
          // 如果配置未建立，则以当前的数据为准
            if (tableDb === null) {
              localforage.setItem(dbTableName, pageTableFields.value).then(() => {
                tableDbIsReady.value = true
              })
            } else {
            // 配置数据存在，则对比差异
            // 新已存在的字段
              const newFieldList = []
              // 不存在的字段
              const nonExistent = []
              // 不存在的类型字段
              const nonExistentTypeList = []
              pageTableFields.value.forEach((field) => {
                if (field.type) {
                  const typeKey = tableDb.findIndex(el => el.type === field.type)
                  if (typeKey === -1) {
                    nonExistentTypeList.push(field)
                  } else {
                    newFieldList.splice(typeKey, 0, field)
                  }
                } else {
                  const propKey = tableDb.findIndex(el => el.prop === field.prop)
                  if (propKey === -1) {
                    nonExistent.push(field)
                  } else {
                    newFieldList.splice(propKey, 0, field)
                  }
                }
              })
              // type 字段放头部
              nonExistentTypeList.push(...newFieldList, ...nonExistent)
              localforage.setItem(dbTableName, nonExistentTypeList).then(() => {
                tableDbIsReady.value = true
              })
            }
          })
        } else {
          tableDbIsReady.value = true
        }
      })
    }
    const handleCustorm = () => {
      // 点击自定义按钮展示操作区
      visible.value = true
      updateCustomList()
    }
    function updateCustomList() {
      localforage.getItem(dbFormName).then((res: [object]) => {
        customFormList.value = res
      })
      localforage.getItem(dbTableName).then((res: [object]) => {
        customTableList.value = res
      })
    }
    // 当拖动到不展示的项时取消操作
    const checkMove = (e) => e.relatedContext.element.draggable
    const handleCustomFormChange = (e) => {
      // 把不显示的置底
      e.show = !e.show
      customFormList.value.sort((a, b) => Number(b.show) - Number(a.show))
    }
    const handleCustomTableChange = (e) => {
      // 把不显示的置底
      e.show = !e.show
      customTableList.value.sort((a, b) => Number(b.show) - Number(a.show))
    }
    const customReset = async () => {
      // 重置配置
      await localforage.setItem(dbFormName, pageFormFields.value)
      await localforage.setItem(dbTableName, pageTableFields.value)
      customFormRef.value.reFreshCustomFO()
      customTableRef.value.reFreshTableFO()
      updateCustomList()
      visible.value = false
    }
    const onCustomSave = async () => {
      const formData = JSON.parse(JSON.stringify(customFormList.value))
      const tableData = JSON.parse(JSON.stringify(customTableList.value))
      visible.value = false
      await localforage.setItem(dbFormName, formData)
      await localforage.setItem(dbTableName, tableData)
      customFormRef.value.reFreshCustomFO()
      customTableRef.value.reFreshTableFO()
    }
    // #endregion
    return {
      drag,
      putAway,
      resetQuery,
      getList,
      visible,
      loading,
      tableData,
      showSearch,
      popoverRef,
      customFormRef,
      customTableRef,
      total,
      handleCustorm,
      customFormList,
      handleCustomFormChange,
      handleCustomTableChange,
      checkMove,
      customReset,
      onCustomSave,
      customTableList,
      formDbIsReady,
      tableDbIsReady,
      updateLimit,
      updatePage,
      columnCount,
      extend,
      seleProps,
    }
  },
})
</script>
<style lang="less" scoped>
.app-container {
  padding: 16px 16px 0;
}
.container-card {
  background-color: #fff;
  border-radius: 10px;
  padding: 16px;
  overflow: hidden;
}
.mt16 {
  margin-top: 16px;
}
.container-operation {
  margin-bottom: 8px;
}
.custom-head {
  display: flex;
  justify-content: space-between;
}
.custom-container {
  overflow: hidden;
}
</style>